import { _decorator, Component, Node, v3, Vec2, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

/**
 * 任务结构体
 */
interface AStarTask {
  startPoint: Vec2;
  endPoint: Vec2;
  callback: (result: Vec3) => void;
  maxDepth?: number;
}

@ccclass('AxingManager')
export class AxingManager {
  static _instance: AxingManager = new AxingManager();
  static getInstance(): AxingManager {
    if (this._instance == undefined) {
      this._instance = new this();
    }
    return this._instance;
  }

  private _pathFindingQueue: AStarTask[] = [];
  private _isProcessing: boolean = false;
  private _maxTasksPerFrame: number = 3; // 每帧最多处理的任务数
  private _frameInterval: number = 1 / 60; // 帧间隔时间
  private _lastProcessTime: number = 0;
  private _defaultMaxDepth: number = 1000; // 默认最大寻路深度
  private useFrameQueue: boolean = false; // 是否使用帧队列

  public setUseFrameQueue(flag: boolean) {
    this.useFrameQueue = flag;
  }

  // 添加任务到队列
  public addPathFindingTask(
    startPoint: Vec2,
    endPoint: Vec2,
    callback: (result: Vec3) => void,
    maxDepth?: number
  ): void {
    const task: AStarTask = {
      startPoint,
      endPoint,
      callback,
      maxDepth: maxDepth || this._defaultMaxDepth,
    };

    if (this.useFrameQueue) {
      this._pathFindingQueue.push(task);
      if (!this._isProcessing) {
        this.processPathFindingQueue();
      }
    } else {
      // 直接执行寻路，不加入队列
      const result = this.Astar_use(startPoint, endPoint, maxDepth);
      callback(result);
    }
  }

  // 处理队列中的任务
  private async processPathFindingQueue(): Promise<void> {
    if (this._pathFindingQueue.length === 0) {
      this._isProcessing = false;
      return;
    }

    this._isProcessing = true;
    const currentTime = Date.now();

    if (currentTime - this._lastProcessTime < this._frameInterval * 1000) {
      setTimeout(
        () => this.processPathFindingQueue(),
        this._frameInterval * 1000
      );
      return;
    }

    let processedTasks = 0;
    while (
      this._pathFindingQueue.length > 0 &&
      processedTasks < this._maxTasksPerFrame
    ) {
      const task = this._pathFindingQueue.shift();
      if (task) {
        const result = this.Astar_use(
          task.startPoint,
          task.endPoint,
          task.maxDepth
        );
        task.callback(result);
        processedTasks++;
      }
    }

    this._lastProcessTime = currentTime;

    // 如果队列中还有任务，在下一帧继续处理
    if (this._pathFindingQueue.length > 0) {
      setTimeout(
        () => this.processPathFindingQueue(),
        this._frameInterval * 1000
      );
    } else {
      this._isProcessing = false;
    }
  }

  // 清空寻路队列
  public clearPathFindingQueue(): void {
    this._pathFindingQueue = [];
    this._isProcessing = false;
  }

  // 获取当前队列长度
  public getQueueLength(): number {
    return this._pathFindingQueue.length;
  }

  public _mapArray: Array<string | number> = new Array();

  public init(): void {}

  private Array2D(w, h, num) {
    var data = [];
    var default_num = num || 0;
    for (var x = 0; x < h; x++) {
      var temp = [];
      for (var y = 0; y < w; y++) {
        temp.push(default_num);
      }
      data.push(temp);
    }
    return {
      w: w,
      h: h,
      data: data,

      showArray2D: function () {
        var s = '';
        for (var y = 0; y < this.h; y++) {
          for (var x = 0; x < this.w; x++) {
            s += this.data[x][y] + ' ';
          }
          s += '\n';
        }
        console.log(s);
      },
    };
  }

  //点结构
  private Point(x: number, y: number) {
    return {
      x: x,
      y: y,
      eq: function (other) {
        return this.x === other.x && this.y === other.y;
      },
    };
  }

  //节点结构
  public Node(point, endPoint, g?) {
    var tG = g || 0;
    return {
      point: point,
      father: null,
      g: tG,
      h: (Math.abs(endPoint.x - point.x) + Math.abs(endPoint.y - point.y)) * 10,
    };
  }

  /**
   * 创建AStar对象，进行寻路
   * @param map2d 地图数据
   * @param startPoint 寻路起点
   * @param endPoint 寻路终点
   * @param passTag 可行走标记
   * @param self 寻路管理器
   * @returns
   */
  private AStar(map2d, startPoint, endPoint, passTag, self: AxingManager) {
    return {
      map2d: map2d,
      startPoint: startPoint,
      endPoint: endPoint,
      passTag: passTag,
      openList: [], //开启表
      closeList: [], //关闭表

      // 计算节点的F值
      getF: function (node) {
        return node.g + node.h;
      },

      // 优化：二分查找插入位置
      findInsertPosition: function (node) {
        const nodeF = this.getF(node);
        let left = 0;
        let right = this.openList.length - 1;

        while (left <= right) {
          const mid = Math.floor((left + right) / 2);
          const midF = this.getF(this.openList[mid]);

          if (midF < nodeF) {
            right = mid - 1;
          } else {
            left = mid + 1;
          }
        }
        return left;
      },

      // 优化：添加节点到openList
      addToOpenList: function (node) {
        const pos = this.findInsertPosition(node);
        this.openList.splice(pos, 0, node);
      },

      // 优化：更新openList中的节点
      updateOpenListNode: function (node, newG, newFather) {
        // 先从openList中移除节点
        const index = this.openList.indexOf(node);
        if (index !== -1) {
          this.openList.splice(index, 1);
        }

        // 更新节点的g值和父节点
        node.g = newG;
        node.father = newFather;

        // 重新插入到正确的位置
        this.addToOpenList(node);
      },

      // 修改搜索周围节点的逻辑
      searchNear: function (minF, offsetX, offsetY) {
        const newX = minF.point.x + offsetX;
        const newY = minF.point.y + offsetY;

        // 边界检查
        if (
          newX < 0 ||
          newX >= this.map2d.w ||
          newY < 0 ||
          newY >= this.map2d.h
        ) {
          return null;
        }

        // 障碍检查 (0表示可通行)
        if (this.map2d.data[newY][newX] !== 0) {
          return null;
        }

        // 如果在关闭表中就忽略
        const currentPoint = self.Point(newX, newY);
        if (this.pointInCloseList(currentPoint)) {
          return null;
        }

        // 计算新的g值
        const step = offsetX === 0 || offsetY === 0 ? 10 : 14;
        const newG = minF.g + step;

        // 检查是否在开启列表中
        const currentNode = this.pointInOpenList(currentPoint);
        if (!currentNode) {
          // 如果不在开启列表中，创建新节点并添加
          const newNode = self.Node(currentPoint, this.endPoint, newG);
          newNode.father = minF;
          this.addToOpenList(newNode);
        } else if (newG < currentNode.g) {
          // 如果新路径更好，使用 updateOpenListNode 更新节点
          this.updateOpenListNode(currentNode, newG, minF);
        }
      },

      //判断point是否在关闭表中
      pointInCloseList: function (point) {
        for (var node of this.closeList) {
          if (node.point.eq(point)) return true;
        }
        return false;
      },

      //判断point是否在开启表中
      pointInOpenList: function (point) {
        for (var node of this.openList) {
          if (node.point.eq(point)) return node;
        }
        return null;
      },

      //判断终点是否在闭表中
      endPointInCloseList: function () {
        for (var node of this.closeList) {
          if (node.point.eq(this.endPoint)) return node;
        }
        return null;
      },

      //开始寻路
      start: function (maxDepth?: number) {
        this.openList = [];
        this.closeList = [];

        const startNode = self.Node(this.startPoint, this.endPoint);
        this.addToOpenList(startNode);

        let iterations = 0;
        while (this.openList.length > 0) {
          iterations++;

          if (maxDepth && this.closeList.length >= maxDepth) {
            console.log('达到最大深度限制:', maxDepth);
            return null;
          }

          const minF = this.getMinNode();
          if (!minF) break;

          this.closeList.push(minF);
          this.openList.pop();

          // 先检查是否到达终点
          if (minF.point.eq(this.endPoint)) {
            // console.log('找到路径，总迭代次数:', iterations);
            return this.buildPath(minF);
          }

          // 搜索八个方向
          this.searchNear(minF, 0, -1);
          this.searchNear(minF, 0, 1);
          this.searchNear(minF, -1, 0);
          this.searchNear(minF, 1, 0);
          this.searchNear(minF, -1, -1);
          this.searchNear(minF, -1, 1);
          this.searchNear(minF, 1, -1);
          this.searchNear(minF, 1, 1);
        }
        // console.log('未找到路径，总迭代次数:', iterations);
        return null;
      },

      // 添加 buildPath 方法
      buildPath: function (endNode) {
        const path = [];
        let current = endNode;
        while (current.father) {
          path.push(current.point);
          current = current.father;
        }
        return path.reverse();
      },

      getMinNode: function () {
        if (this.openList.length === 0) return null;
        return this.openList[this.openList.length - 1];
      },
    };
  }

  // 地图数据
  public map2d;
  public initMapData_zhangaiwu(width, height, mapData): void {
    this.map2d = this.Array2D(width, height, 0);
    for (let i = 0; i < height; i++) {
      for (let j = 0; j < width; j++) {
        this.map2d.data[i][j] = mapData[i * width + j];
      }
    }
  }

  /**
   * tiledMap地图数据转换为二维数组
   * @param width 地图宽度
   * @param height
   * @param mapData
   * @returns
   */
  public tilesToMapData(width, height, mapData): number[] {
    var data = [];
    var default_num = 0;
    for (var x = 0; x < height; x++) {
      var temp = [];
      for (var y = 0; y < width; y++) {
        temp.push(default_num);
      }
      data.push(temp);
    }
    for (let i = 0; i < height; i++) {
      for (let j = 0; j < width; j++) {
        data[i][j] = mapData[i * width + j] == 0 ? 1 : 0;
      }
    }
    return data;
  }

  /**
   * 添加地图数据
   * @param h 地图高度
   * @param w 地图宽度
   * @param mapData 地图数据
   * @param dir 地图方向
   */
  addMapData(h, w, mapData, dir) {
    let newMapData = this.Array2D(w, h, 0);
    for (let i = 0; i < w; i++) {
      for (let j = 0; j < h; j++) {
        newMapData.data[i][j] = mapData[i * w + j] == 0 ? 1 : 0;
      }
    }

    // 根据方向进行合并
    switch (dir) {
      case 0: //'up':
        // 在现有地图的上方加新地图
        this.map2d.w = Math.max(this.map2d.w, w);
        this.map2d.h += h;

        // 插入新的行到this.map2d顶部
        for (let j = 0; j < h; j++) {
          this.map2d.data.unshift(newMapData.data[j]);
        }
        break;
      case 1: // 'right':
        // 在现有地图的右边添加新地图
        this.map2d.w += w;
        this.map2d.h = Math.max(this.map2d.h, h);

        for (let j = 0; j < this.map2d.h; j++) {
          let newRow = j < h ? newMapData.data[j] : Array(w).fill(0);
          this.map2d.data[j] = this.map2d.data[j].concat(newRow);
        }
        break;

      case 2: //'down':
        // 在现有地图的下方添加新地图
        this.map2d.w = Math.max(this.map2d.w, w);
        this.map2d.h += h;

        // 插入新的行到this.map2d底部
        for (let j = 0; j < h; j++) {
          this.map2d.data.push(newMapData.data[j]);
        }
        break;

      case 3: //'left':
        // 在现有地图的左边添加新地图
        this.map2d.w += w;
        this.map2d.h = Math.max(this.map2d.h, h);

        // 对每一行，在左边插入新的数据
        for (let j = 0; j < this.map2d.h; j++) {
          let newRow = j < h ? newMapData.data[j] : Array(w).fill(0);
          this.map2d.data[j] = newRow.concat(this.map2d.data[j]);
        }
        break;

      default:
        console.error('Invalid direction:', dir);
    }
  }

  private _mapCache: Record<string, Vec3> = {}; // 缓存寻路结果

  // 修改 Astar_use 方法
  private Astar_use(
    start_point: Vec2,
    to_point: Vec2,
    maxDepth: number = this._defaultMaxDepth
  ): Vec3 {
    // console.log(
    //   '开始寻路:',
    //   '起点:',
    //   start_point,
    //   '终点:',
    //   to_point,
    //   '地图大小:',
    //   this.map2d.w,
    //   'x',
    //   this.map2d.h
    // );

    // 查点是否有效
    if (!this.isValidPoint(start_point)) {
      console.log('起点无效');
      return Vec3.ZERO;
    }
    if (!this.isValidPoint(to_point)) {
      console.log('终点无效');
      return Vec3.ZERO;
    }

    // 检查缓存
    const cacheKey = `${start_point.x},${start_point.y},${to_point.x},${to_point.y}`;
    if (this._mapCache[cacheKey]) {
      return this._mapCache[cacheKey];
    }

    const start_row = Math.floor(start_point.x);
    const start_column = Math.floor(start_point.y);
    const to_row = Math.floor(to_point.x);
    const to_column = Math.floor(to_point.y);

    // 创建 AStar 实例并寻路
    const aStar = this.AStar(
      this.map2d,
      this.Point(start_row, start_column),
      this.Point(to_row, to_column),
      0,
      this
    );

    const pathList = aStar.start(maxDepth);
    if (!pathList || pathList.length === 0) {
      return Vec3.ZERO;
    }

    // 计算移动向量
    const firstPoint = v3(pathList[0].row, pathList[0].column, 0);
    const moveVector = firstPoint.subtract(v3(start_row, start_column, 0));
    const result = v3(moveVector.y, moveVector.x * -1, 0);

    // 缓存结果
    this._mapCache[cacheKey] = result;
    return result;
  }

  // 添加点检查方法
  isValidPoint(point: Vec2): boolean {
    if (!this.map2d || !this.map2d.data) {
      console.log('地图数据未初始化');
      return false;
    }
    const row = Math.floor(point.x);
    const col = Math.floor(point.y);

    // 检查边界
    if (row < 0 || row >= this.map2d.h || col < 0 || col >= this.map2d.w) {
      console.log(`点 (${row}, ${col}) 超出边界`);
      return false;
    }

    // 检查是否可通行（0表示可通行）
    const isPassable = this.map2d.data[row][col] === 0;
    if (!isPassable) {
      console.log(`点 (${row}, ${col}) 是障碍物`);
    }
    return isPassable;
  }

  /**
   * 使用A*算法寻路
   * @param start 起点
   * @param end 终点
   * @returns
   */
  public findPath(start: Vec2, end: Vec2): Promise<Vec3> {
    return new Promise((resolve) => {
      this.addPathFindingTask(start, end, (result) => {
        resolve(result);
      });
    });
  }

  // 清除缓存方法
  public clearCache(): void {
    this._mapCache = {};
  }

  public dispose(): void {}
}
